/*
 * @Description: 代码预览
 * @Author: DHL
 * @Date: 2021-12-25 11:59:24
 * @LastEditors: DHL
 * @LastEditTime: 2022-10-08 14:11:09
 */
import { cloneDeep, merge } from 'lodash-es'
import { defineComponent, ref, reactive, nextTick } from 'vue'
import { saveAs } from 'file-saver'
import JSZip from 'jszip'
import { createController } from './resources/controller/Controller'
import { createBean } from './resources/entity/bean'
import { createBeanDto } from './resources/entity/dto/beanDto'
import { createExportExcelDto } from './resources/entity/dto/ExportExcelDto'
import { createGetByIdDto } from './resources/entity/dto/GetByIdDto'
import { createSearchDto } from './resources/entity/dto/SearchDto'
import { createSaveVo } from './resources/entity/vo/SaveVo'
import { createSearchVo } from './resources/entity/vo/SearchVo'
import { createUpdateByIdVo } from './resources/entity/vo/UpdateByIdVo'
import { createMapper } from './resources/mapper/Mapper'
import { createMapperXml } from './resources/mapper/MapperXml'
import { createIService } from './resources/service/IService'
import { createService } from './resources/service/Service'
import { createSql } from './resources/sql/Sql'
import { createApi } from './resources/vue/api/Api'
import { createModel } from './resources/vue/api/Model'
import { createForm } from './resources/vue/Form'
import { createIndex } from './resources/vue/Index'
import { createUseForm } from './resources/vue/UseForm'
import { createUseIndex } from './resources/vue/UseIndex'
import { getDateTime } from '/@/utils/dateUtils'
import { firstToLowerCase } from './utils/codeGenUtils'
import { log } from '/@/utils/log'
import { stringUtils } from '/@/utils/stringUtils'

import * as monaco from 'monaco-editor'
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'

export default defineComponent({
  name: 'SystemToolsCodeGenPreview',
  setup(props, { emit, expose }) {
    //////////////////////////////////////////////////
    // 属性
    //////////////////////////////////////////////////
    const codeGenConfig = reactive({} as any)

    const state = reactive({
      activeMenu: 'Bean',
      codeType: 'Bean'
    })

    // 解决monaco控制台异常： Unexpected usage
    ;(self as any).MonacoEnvironment = {
      getWorker(_: any, label: any) {
        if (label === 'json') {
          return new jsonWorker()
        }
        if (label === 'css' || label === 'scss' || label === 'less') {
          return new cssWorker()
        }
        if (label === 'html' || label === 'handlebars' || label === 'razor') {
          return new htmlWorker()
        }
        if (label === 'typescript' || label === 'javascript') {
          return new tsWorker()
        }
        return new editorWorker()
      }
    }

    const codeMonacoRef = ref()
    let codeMonacoInstance = {} as monaco.editor.IStandaloneCodeEditor

    // 代码生成配置
    const codeGenOptions = reactive({
      Bean: {
        type: 'java',
        fileName: '{0}.java',
        path: 'main/java/{0}/entity/{1}/',
        code: () => {
          return createBean(codeGenConfig)
        }
      },
      BeanDto: {
        type: 'java',
        fileName: '{0}Dto.java',
        path: 'main/java/{0}/entity/{1}/dto/',
        code: () => {
          return createBeanDto(cloneDeep(codeGenConfig))
        }
      },
      SaveVo: {
        type: 'java',
        fileName: 'Save{0}Vo.java',
        path: 'main/java/{0}/entity/{1}/vo/',
        code: () => {
          return createSaveVo(cloneDeep(codeGenConfig))
        }
      },
      SearchVo: {
        type: 'java',
        fileName: 'Search{0}Vo.java',
        path: 'main/java/{0}/entity/{1}/vo/',
        code: () => {
          return createSearchVo(cloneDeep(codeGenConfig))
        }
      },
      UpdateByIdVo: {
        type: 'java',
        fileName: 'Update{0}ByIdVo.java',
        path: 'main/java/{0}/entity/{1}/vo/',
        code: () => {
          return createUpdateByIdVo(cloneDeep(codeGenConfig))
        }
      },
      SearchDto: {
        type: 'java',
        fileName: 'Search{0}Dto.java',
        path: 'main/java/{0}/entity/{1}/dto/',
        code: () => {
          return createSearchDto(cloneDeep(codeGenConfig))
        }
      },
      GetByIdDto: {
        type: 'java',
        fileName: 'Get{0}ByIdDto.java',
        path: 'main/java/{0}/entity/{1}/dto/',
        code: () => {
          return createGetByIdDto(cloneDeep(codeGenConfig))
        }
      },
      /*ExportExcelDto: {
        type: 'java',
        fileName: 'ExportExcel{0}Dto.java',
        path: 'main/java/{0}/entity/{1}/dto/',
        code: () => {
          return createExportExcelDto(cloneDeep(codeGenConfig))
        }
      },*/
      Controller: {
        type: 'java',
        fileName: '{0}Controller.java',
        path: 'main/java/{0}/controller/',
        code: () => {
          return createController(cloneDeep(codeGenConfig))
        }
      },
      IService: {
        type: 'java',
        fileName: 'I{0}Service.java',
        path: 'main/java/{0}/service/',
        code: () => {
          return createIService(cloneDeep(codeGenConfig))
        }
      },
      Service: {
        type: 'java',
        fileName: '{0}Service.java',
        path: 'main/java/{0}/service/impl/',
        code: () => {
          return createService(cloneDeep(codeGenConfig))
        }
      },
      Mapper: {
        type: 'java',
        fileName: '{0}Mapper.java',
        path: 'main/java/{0}/mapper/',
        code: () => {
          return createMapper(cloneDeep(codeGenConfig))
        }
      },
      MapperXml: {
        type: 'xml',
        fileName: '{0}Mapper.xml',
        path: 'main/java/{0}/mapper/xml/',
        code: () => {
          return createMapperXml(cloneDeep(codeGenConfig))
        }
      },
      index: {
        type: 'xml',
        fileName: 'index.vue',
        path: 'main/vue/{0}/{1}/',
        code: () => {
          return createIndex(cloneDeep(codeGenConfig))
        }
      },
      form: {
        type: 'xml',
        fileName: 'form.vue',
        path: 'main/vue/{0}/{1}/',
        code: () => {
          return createForm(cloneDeep(codeGenConfig))
        }
      },
      useIndex: {
        type: 'typescript',
        fileName: 'useIndex.ts',
        path: 'main/vue/{0}/{1}/',
        code: () => {
          return createUseIndex(cloneDeep(codeGenConfig))
        }
      },
      useForm: {
        type: 'typescript',
        fileName: 'useForm.ts',
        path: 'main/vue/{0}/{1}/',
        code: () => {
          return createUseForm(cloneDeep(codeGenConfig))
        }
      },
      model: {
        type: 'typescript',
        fileName: '{0}Model.ts',
        path: 'main/vue/api/{0}/model/',
        code: () => {
          return createModel(cloneDeep(codeGenConfig))
        }
      },
      api: {
        type: 'typescript',
        fileName: '{0}.ts',
        path: 'main/vue/api/{0}/',
        code: () => {
          return createApi(cloneDeep(codeGenConfig))
        }
      },
      sql: {
        type: 'sql',
        fileName: '{0}.sql',
        path: 'main/sql/',
        code: () => {
          return createSql(cloneDeep(codeGenConfig))
        }
      }
    })

    //////////////////////////////////////////////////
    // 初始化
    //////////////////////////////////////////////////
    function createCode(codeGenConfigParams: any) {
      merge(codeGenConfig, cloneDeep(codeGenConfigParams), {
        createData: getDateTime()
      })

      codeGenConfig.javaPath = codeGenConfig.packageName.replaceAll('.', '/')

      codeGenConfig.apiPath = codeGenConfig.viewPath.substring(
        codeGenConfig.viewPath.indexOf('/') + 1,
        codeGenConfig.viewPath.length
      )
      if (codeGenConfig.apiPath.charAt(codeGenConfig.apiPath.length - 1) === '/') {
        codeGenConfig.apiPath = codeGenConfig.apiPath.substr(0, codeGenConfig.apiPath.length - 1)
      }

      nextTick(() => {
        setTimeout(function () {
          createCodeMonacoInstance()

          state.activeMenu = 'Bean'
          changeModel(createBean(cloneDeep(codeGenConfig)), 'java')
        }, 200)
      })
    }

    /**
     * 创建编辑器实例
     * @returns
     */
    function createCodeMonacoInstance() {
      const options = {
        theme: 'vs-dark'
      }

      if (Object.keys(codeMonacoInstance).length === 0) {
        codeMonacoInstance = monaco.editor.create(codeMonacoRef.value, options)
      }
    }

    /**
     * 更改编辑器语言
     */
    function changeModel(value: string, language: string) {
      const oldModel = codeMonacoInstance.getModel() //获取旧模型
      if (oldModel) {
        oldModel.dispose()
      }
      const newModel = monaco.editor.createModel(value, language)
      codeMonacoInstance.setModel(newModel)
    }

    /**
     * 菜单点击
     */
    function handleMenuItemClick(menuType: string) {
      state.codeType = menuType

      let codeInfo: any = {}

      for (const [key, val] of Object.entries(codeGenOptions)) {
        if (key === menuType) {
          codeInfo = val
        }
      }

      changeModel(codeInfo.code(), codeInfo.type)
    }

    /**
     * 下载单个文件
     */
    function handleDownloadFile() {
      const classNameFirstToLowerCase = firstToLowerCase(codeGenConfig.className)

      for (const [key, val] of Object.entries(codeGenOptions)) {
        if (key === state.codeType) {
          let fileName = stringUtils.format(val.fileName, codeGenConfig.className)

          if (['index', 'form', 'useIndex', 'useForm', 'model', 'api', 'sql'].includes(key)) {
            fileName = stringUtils.format(val.fileName, classNameFirstToLowerCase)
          }

          const blob = new Blob([val.code()], { type: 'text/plain;charset=utf-8' })
          saveAs(blob, fileName)
        }
      }
    }

    /**
     * 下载zip包
     */
    function handleDownloadZip() {
      const zip = new JSZip()
      const classNameFirstToLowerCase = firstToLowerCase(codeGenConfig.className)

      for (const [key, val] of Object.entries(codeGenOptions)) {
        let fileName = stringUtils.format(
          val.path,
          codeGenConfig.javaPath,
          classNameFirstToLowerCase
        )
        fileName += stringUtils.format(val.fileName, codeGenConfig.className)

        if (['index', 'form', 'useIndex', 'useForm', 'model', 'api', 'sql'].includes(key)) {
          fileName = stringUtils.format(val.path, codeGenConfig.apiPath, classNameFirstToLowerCase)
          fileName += stringUtils.format(val.fileName, classNameFirstToLowerCase)
        }

        zip.file(fileName, val.code())
      }

      zip
        .generateAsync({
          type: 'blob'
        })
        .then(
          (blob) => {
            saveAs(blob, `代码生成_${codeGenConfig.genName}.zip`)
          },
          (err) => {
            log.warn('代码生成管理', ['导出失败', err])
          }
        )
    }

    /**
     * 暴露函数
     */
    expose({ createCode, handleDownloadZip, handleDownloadFile })

    return {
      state,
      codeMonacoRef,
      handleMenuItemClick,
      handleDownloadZip,
      handleDownloadFile
    }
  }
})
